import paho.mqtt.client as mqtt
import json
import logging
import time
import os
import configparser
from datetime import datetime
import msgpack
from collections import OrderedDict
import csv
import threading

# ------------------------ 配置管理 ------------------------
class ConfigManager:
    """统一管理所有配置参数"""
    def __init__(self):
        self.config = configparser.ConfigParser()
        with open('config.ini', encoding='utf-8') as f:
            self.config.read_file(f)
        
    @property
    def mqtt_config(self) -> dict:
        return dict(self.config['MQTT'])
        
    @property
    def app_config(self) -> dict:
        return {
            'log_dir': self.config['APP']['LogDir'],
            'csv_file': self.config['APP'].get('CSVFile', 'devices.csv')
        }

# ------------------------ 日志管理 ------------------------
class LogManager:
    """日志配置管理"""
    def __init__(self, log_dir: str):
        self.log_dir = log_dir
        self._ensure_directory()
        self.logger = self._configure_logger()
        
    def _ensure_directory(self):
        if not os.path.exists(self.log_dir):
            os.makedirs(self.log_dir)
            
    def _configure_logger(self):
        log_file = os.path.join(
            self.log_dir, 
            f"mqtt_monitor_{datetime.now().strftime('%Y%m%d')}.log"
        )
        
        logger = logging.getLogger('MQTTMonitor')
        logger.setLevel(logging.INFO)
        
        formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
        
        file_handler = logging.FileHandler(log_file)
        file_handler.setFormatter(formatter)
        
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(formatter)
        
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        
        return logger

# ------------------------ 数据处理模块 ------------------------
class MQTTDataProcessor:
    """专业级MQTT数据处理器"""
    def __init__(self, logger: logging.Logger):
        self.logger = logger
        
    def process(self, payload: bytes, type:int) -> dict:
        """主数据处理入口"""
        try:
            if type == 1:
                return self._process_msgpack(payload)
            elif type == 2:
                return self._process_json(payload)
        except Exception as e:
            self.logger.error(f"数据处理失败: {str(e)}")
            return {"error": str(e)}
    
    def _process_json(self, payload: bytes) -> dict:
        try:
            data = json.loads(payload.decode())
            # print(data)
            result = OrderedDict()
            gateway_mac_str = data.get('mac','')
            result['gateway_mac'] = self._format_mac(gateway_mac_str)
            result['devices'] = []
            for device_entry in data.get('devices', []):
                if device_entry:
                    # 解析设备字节数据
                    if len(device_entry) >= 4:
                        mac_bytes = device_entry[2]  # 提取byte2-7（索引1到6）
                        rssi = device_entry[3]
                        # 转换MAC地址为十六进制字符串
                        mac = self._format_mac(mac_bytes)
                        # 转换RSSI为有符号整数
                        # 添加到设备列表
                        result['devices'].append({
                            'mac': mac,
                            'rssi': rssi
                        })
                    else:
                        self.logger.error(f"设备数据长度不足: {len(device_entry)}")
                else:
                    self.logger.error(f"无效的设备数据类型: {type(device_entry)}")            
            print(result)
            return result
            
        except Exception as e:
            self.logger.error(f"json解析异常: {str(e)}", exc_info=True)
            return {"error": "Invalid json"}
    
    
    def _process_msgpack(self, payload: bytes) -> dict:
        """处理Msgpack协议数据"""
        try:
            data = msgpack.unpackb(payload, strict_map_key=False)
            # print(data)
            result = OrderedDict()
            # print("Keys in data:", data.keys())
            # print("Type of 'mac' key:", type(next(iter(data.keys())))) 
            # 解析网关信息
            gateway_mac_str = data.get('mac', '')
            result['gateway_mac'] = self._format_mac(gateway_mac_str)
            result['devices'] = []
            
            # 解析设备数据
            for device_bytes in data.get('devices', []):
                if isinstance(device_bytes, bytes):
                    # 解析设备字节数据
                    if len(device_bytes) >= 8:
                        mac_bytes = device_bytes[1:7]  # 提取byte2-7（索引1到6）
                        rssi_byte = device_bytes[7]
                        # 转换MAC地址为十六进制字符串
                        mac = mac_bytes.hex().upper()
                        # 转换RSSI为有符号整数
                        rssi = rssi_byte if rssi_byte <= 127 else rssi_byte - 256
                        # 添加到设备列表
                        result['devices'].append({
                            'mac': mac,
                            'rssi': rssi
                        })
                    else:
                        self.logger.error(f"设备数据长度不足: {len(device_bytes)}")
                else:
                    self.logger.error(f"无效的设备数据类型: {type(device_bytes)}")
            print(result)
            return result
        except Exception as e:
            self.logger.error(f"Msgpack解析异常: {str(e)}", exc_info=True)
            return {"error": "Invalid Msgpack"}

    def _parse_ble_packet(self, data: bytes) -> OrderedDict:
        """解析BLE广播包"""
        if len(data) < 9:
            return None
            
        try:
            return OrderedDict([
                ('packet_type', data[0]),
                ('ble_mac', self._format_ble_mac(data[1:7])),
                ('rssi', self._parse_rssi(data[7])),
                ('advertisement', self._parse_advertisement(data[8:])),
                ('raw', data.hex().upper())
            ])
        except IndexError:
            self.logger.warning("BLE数据包格式异常")
            return None

    def _parse_advertisement(self, data: bytes) -> list:
        """解析广播数据段"""
        adv_data = []
        pos = 0
        
        while pos < len(data):
            if pos + 1 >= len(data):
                break
                
            length = data[pos]
            if length == 0 or (pos + length) > len(data):
                break
                
            adv_type = data[pos+1]
            value = data[pos+2:pos+1+length]
            
            adv_data.append({
                'type': f"0x{adv_type:02X}",
                'length': length,
                'value': value.hex().upper()
            })
            
            pos += length + 1
            
        return adv_data

    def _format_mac(self, mac_str: str) -> str:
        """标准化MAC地址格式"""
        clean = ''.join(c for c in mac_str if c.isalnum())
        return ':'.join(clean[i:i+2] for i in range(0, 12, 2)).upper()

    def _format_ble_mac(self, mac_bytes: bytes) -> str:
        """BLE MAC地址解析（小端转标准）"""
        return ':'.join(f"{b:02X}" for b in reversed(mac_bytes))

    def _parse_rssi(self, byte_val: int) -> int:
        """RSSI值计算"""
        return byte_val - 256 if byte_val > 127 else byte_val

# ------------------------ MQTT客户端管理 ------------------------
class MQTTClientManager:
    """增强型MQTT客户端"""
    def __init__(self, config: dict, logger: logging.Logger, log_dir: str, csv_file: str):
        self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
        self.logger = logger
        self.data_processor = MQTTDataProcessor(logger)
        
        # 新增路径相关属性
        self.log_dir = log_dir
        self.csv_file = os.path.join(log_dir, csv_file)
        self.csv_lock = threading.Lock()  # 初始化线程锁
        
        self._configure_client(config)
        self._connected = False

    def _configure_client(self, config: dict):
        """客户端配置"""
        self.client.on_connect = self._on_connect
        self.client.on_message = self._on_message
        self.client.on_disconnect = self._on_disconnect
        
        # 连接参数设置
        if 'username' in config and 'password' in config:
            self.client.username_pw_set(
                config['username'],
                config['password']
            )
            
        # 自动重连设置
        self.client.reconnect_delay_set(min_delay=5, max_delay=60)
        
        # 异步连接
        self.client.connect_async(
            config['host'],
            int(config.get('port', 1883)),
            int(config.get('keepalive', 60))
        )

    def _on_connect(self, client, userdata, flags, reason_code, properties):
        """新版回调参数规范"""
        success_codes = [0, mqtt.CONNACK_ACCEPTED]
        if reason_code in success_codes:
            client.subscribe("gateway")
            self.logger.info("成功连接MQTT服务器，已订阅gateway主题")
        else:
            error_msg = {
                1: "协议版本错误",
                2: "客户端ID无效",
                3: "服务器不可用",
                4: "用户名密码错误",
                5: "未授权"
            }.get(reason_code, "未知错误")
            self.logger.error(f"连接失败: {error_msg}")

    def _on_message(self, client, userdata, msg):
        """消息处理核心"""
        try:
            if msg.topic != "gateway":
                return

            # 数据处理流程
            processed = self.data_processor.process(msg.payload, 2)
            
            if 'error' in processed:
                self.logger.error(f"数据解析失败: {processed['error']}")
                return
                
            # 记录结构化数据
            # self._log_processed_data(processed)
            self._save_to_csv(processed)  # 新增CSV保存逻辑            
                
        except Exception as e:
            self.logger.error(f"消息处理异常: {str(e)}", exc_info=True)

    def _log_processed_data(self, data: dict):
        """结构化日志记录"""
        log_entry = [
            f"网关MAC: {data.get('gateway_mac', 'N/A')}",
            f"检测到{len(data.get('devices', []))}个BLE设备:"
        ]
        
        for i, dev in enumerate(data.get('devices', []), 1):
            log_entry.append(
                # f"设备#{i}: MAC={dev.get('ble_mac')} "
                f"RSSI={dev.get('rssi')}dBm"
            )
            
        self.logger.info("\n".join(log_entry))

    def _save_to_csv(self, data: dict):
        try:
            fieldnames = ['timestamp', 'gateway_mac', 'device_mac', 'rssi']
            
            # 确保目录存在
            os.makedirs(self.log_dir, exist_ok=True)
            
            with self.csv_lock:  # 线程安全写入
                file_exists = os.path.exists(self.csv_file)
                
                with open(self.csv_file, 'a', newline='') as f:
                    writer = csv.DictWriter(f, fieldnames=fieldnames)
                    
                    # 写入表头（仅当文件新建时）
                    if not file_exists or f.tell() == 0:
                        writer.writeheader()
                    
                    timestamp = datetime.now().isoformat()
                    for device in data.get('devices', []):
                        writer.writerow({
                            'timestamp': timestamp,
                            'gateway_mac': data['gateway_mac'],
                            'device_mac': device['mac'],
                            'rssi': device['rssi']
                        })
        except Exception as e:
            self.logger.error(f"CSV保存失败: {str(e)}")
            
                 
    def _on_disconnect(self, client, userdata, flags, reason_code, properties):
        """断开连接回调"""
        self._connected = False
        if reason_code != mqtt.MQTT_ERR_SUCCESS:
            self.logger.warning(f"意外断开连接，正在尝试重连 (错误码: {reason_code})")
            
    def start(self):
        """启动客户端"""
        self.client.loop_start()
        
    def stop(self):
        """安全关闭客户端"""
        self.client.disconnect()
        self.client.loop_stop()

# ------------------------ 主程序 ------------------------
if __name__ == "__main__":
    # 初始化系统组件
    config = ConfigManager()
    log_manager = LogManager(config.app_config['log_dir'])
    mqtt_client = None
    
    try:
        # 创建客户端实例
        mqtt_client = MQTTClientManager(
            config.mqtt_config,
            log_manager.logger,
            config.app_config['log_dir'],
            config.app_config['csv_file']
        )
        
        # 显示启动信息
        print("\n" + "="*50)
        print(" MQTT物联网设备监控系统 ")
        print(f"日志目录: {os.path.abspath(config.app_config['log_dir'])}")
        print("="*50 + "\n")
        
        # 启动服务
        mqtt_client.start()
        
        # 保持主线程运行
        while True:
            time.sleep(1)
            
    except KeyboardInterrupt:
        log_manager.logger.info("用户主动终止程序")
    except Exception as e:
        log_manager.logger.critical(f"系统异常终止: {str(e)}")
    finally:
        if mqtt_client:  # 安全检查
            mqtt_client.stop()
            log_manager.logger.info("MQTT客户端已安全关闭")